package edu.ntu.cloudroid.client;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.PrintWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TimeZone;

import edu.ntu.cloudroid.model.IDs;
import edu.ntu.cloudroid.model.UserData;
import edu.ntu.cloudroid.model.data.AbstractData;
import edu.ntu.cloudroid.model.data.DetachedData;
import edu.ntu.cloudroid.model.data.VitalSignInfo;
import edu.ntu.cloudroid.model.friend.FriendInfo;
import edu.ntu.cloudroid.model.policy.PolicyInfo;
import edu.ntu.cloudroid.model.profile.ProfileInfo;
import edu.ntu.cloudroid.nongui.com.AbstractResponse;
import edu.ntu.cloudroid.nongui.com.Communicator;
import edu.ntu.cloudroid.nongui.com.LocationRequestWithInputs;
import edu.ntu.cloudroid.nongui.com.LogReply;
import edu.ntu.cloudroid.nongui.com.LogRequest;
import edu.ntu.cloudroid.nongui.com.VitalDataRequestWithInputs;

public class PrepareData {
	public static final String DEFAULT_DATE = "2011-01-01 00:00";
	
	public static void main(String[] args) throws Exception {
		Communicator comm;
		int machineId=0;
		int nMachines=0;
		if (args.length>3){
			comm = new Communicator(null, args[0], Integer.parseInt(args[1]));
			nMachines = new Integer(args[2]).intValue();
			machineId = new Integer(args[3]).intValue();
		}
		else{
			comm = new Communicator(args[0]);
			nMachines = new Integer(args[1]).intValue();
			machineId = new Integer(args[2]).intValue();
		}
		
		//generateRandomUser(comm);
		//uploadFunctions(comm, args[2]);
		Thread.currentThread().sleep(2000 - (1000*machineId));
		
		if (machineId==0)
			purgeCache(comm);
		//getLogs(comm);
		//getLatestLogTimestamp(comm);
		//comm.sendData(new VitalSignInfo("null user_616@gmail.com", null, 100, 100));

		/*LocationRequestWithInputs lrwi = new LocationRequestWithInputs(
				"user_0@gmail.com", "user_4@gmail.com", -122.2, 37.58);
		AbstractResponse ar = comm.getData(lrwi);
		Map<String, Map<String, List<DetachedData>>> rs = ar.getReturnedData();
		for (String s : rs.keySet()){
			System.out.println("For string "+s);
			Map<String, List<DetachedData>> m = rs.get(s);
			for (String s1 : m.keySet()){
				System.out.println("for string :"+s1+", data size = "+m.get(s1).size());
			}
		}*/
		//generateUsersFromFile(comm, nMachines, machineId);	
		deleteProfilesAndFriends(comm,nMachines,machineId);
		//generateData(comm,nMachines,machineId);
		//deleteData(comm,nMachines,machineId);
		//generateFriends(comm,nMachines,machineId);
		//generatePolicies(comm,nMachines,machineId);
		//deletePolicies(comm,nMachines,machineId);
	}
	
	private static void getLatestLogTimestamp(Communicator comm) throws Exception{
		LogRequest lr = new LogRequest("null");
		LogReply reply = (LogReply)comm.getData(lr);
		PrintWriter writer = new PrintWriter("timestamp");
		writer.println(reply.getCurrentOffset());		
		writer.close();
		writer = new PrintWriter("log");
		ArrayList<String> logs = reply.getLogMessages();
		if (logs!=null){
			for (String l: logs)
				writer.print(l);
		}
		writer.close();
	}
	private static void getLogs(Communicator comm) throws Exception{
		//read the latest timestamp
		BufferedReader reader = new BufferedReader(new FileReader("timestamp"));
		LogRequest lr = new LogRequest(reader.readLine());
		reader.close();
		
		//then query the log and update the latest timestamp
		LogReply reply = (LogReply)comm.getData(lr);
		PrintWriter writer = new PrintWriter("timestamp");
		writer.println(reply.getCurrentOffset());		
		writer.close();
		writer = new PrintWriter("log");
		ArrayList<String> logs = reply.getLogMessages();
		if (logs!=null){
			for (String l: logs)
				writer.print(l);
		}
		writer.close();
	}
	
	private static void purgeCache(Communicator comm) throws Exception{
		LogRequest lr = new LogRequest("purge_cache");
		comm.getData(lr);
	}
	
	private static void deleteProfilesAndFriends(Communicator comm, int nMachines, int machineId){
		/*List<UserData> profiles = Generator.generateDeleteProfiles(IDs.DataID.VitalSignData.ordinal(), 1000);
		int nThreads = 25;
		int interval = profiles.size()/nThreads;
		int machineInterval = nThreads/nMachines;
		int startingMachine = machineInterval*machineId; 
		for (int i=startingMachine; i<startingMachine+machineInterval; i++){
			MultipleProfileSender mps = new MultipleProfileSender(comm,profiles,i*interval,interval);
			mps.start();
		}*/

		List<UserData> friends = Generator.generateDeleteFriends(1000);
		int nThreads = 40;
		int interval = friends.size()/nThreads;		
		int machineInterval = nThreads/nMachines;
		int startingMachine = machineInterval*machineId;
		for (int i=startingMachine; i<startingMachine+machineInterval; i++){
			MultipleProfileSender mps = new MultipleProfileSender(comm,friends,i*interval,interval);
			mps.start();
		}	
	}
	
	private static void generateUsersFromFile(Communicator comm, int nMachines,
			int machineId) throws Exception {
		List<UserData> profiles = Generator.generateProfiles(IDs.DataID.LocationData.ordinal(), 700);
		int nThreads = 20;
		int interval = profiles.size()/nThreads;
		int machineInterval = nThreads/nMachines;
		int startingMachine = machineInterval*machineId; 
		for (int i=startingMachine; i<startingMachine+machineInterval; i++){
			MultipleProfileSender mps = new MultipleProfileSender(comm,profiles,i*interval,interval);
			mps.start();
		}
	}

	private static void generatePolicies(Communicator comm, int nMachines, int machineId) throws Exception{
		//List<UserData> policies  = Generator.generatePolicies(IDs.PolicyID.SimpleFiltering.ordinal(), 1000, false);
		List<UserData> policies = Generator.generateLocationSimilarityPolicy(700);
		int nThreads = 25;
		int interval = policies.size()/nThreads;
		int machineInterval = nThreads/nMachines;
		int startingMachine = machineInterval*machineId; 
		for (int i=startingMachine; i<startingMachine+machineInterval; i++){
			MultipleProfileSender mps = new MultipleProfileSender(comm,policies,i*interval,interval);
			mps.start();
		}

	}
	private static void generateFriends(Communicator comm, int nMachines, int machineId) throws Exception{
		List<UserData> friends = Generator.generateFriendsFromFile("dataset/location/friends1000");
		int nThreads = 40;
		int interval = friends.size()/nThreads;		
		int machineInterval = nThreads/nMachines;
		int startingMachine = machineInterval*machineId;
		for (int i=startingMachine; i<startingMachine+machineInterval; i++){
			MultipleProfileSender mps = new MultipleProfileSender(comm,friends,i*interval,interval);
			mps.start();
		}
	}

	private static void generateData(Communicator comm, int nMachines, int machineId) throws Exception{
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
		sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
		Date startingDate = sdf.parse(DEFAULT_DATE);
		
		//List<UserData> data = Generator.generateVitalSignDataFromFile("dataset/physio/data", 1000, startingDate, 10, 20);
		List<UserData> data = Generator.generateLocationDataFromFile(
				"dataset/location/checkin_10_per_person_700_users", 700,
				startingDate, 10, 10);
		int nThreads = 25;
		int interval = data.size()/nThreads;		
		int machineInterval = nThreads/nMachines;
		int startingMachine = machineInterval*machineId;
		for (int i=startingMachine; i<startingMachine+machineInterval; i++){
			MultipleProfileSender mps = new MultipleProfileSender(comm,data,i*interval,interval);
			mps.start();
		}

	}

	private static void deleteData(Communicator comm, int nMachines, int machineId) throws Exception{
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
		sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
		Date startingDate = sdf.parse(DEFAULT_DATE);
		
		List<UserData> deleteList = new ArrayList<UserData>();
		for (int i=0; i<1000; i++){
			deleteList.add(new VitalSignInfo("null user_"+i+"@gmail.com", startingDate, 100, 100));
		}
		int nThreads = 25;
		int interval = deleteList.size()/nThreads;		
		int machineInterval = nThreads/nMachines;
		int startingMachine = machineInterval*machineId;

		for (int i=startingMachine; i<startingMachine+machineInterval; i++){
			MultipleProfileSender mps = new MultipleProfileSender(comm,deleteList,i*interval,interval);
			mps.start();
		}
	
	}

	private static void deletePolicies(Communicator comm, int nMachines, int machineId) throws Exception{
		List<UserData> policies  = Generator.generateDeletePolicy(IDs.PolicyID.SimpleFiltering.ordinal(), 1000, false);
		int nThreads = 25;
		int interval = policies.size()/nThreads;

		int machineInterval = nThreads/nMachines;
		int startingMachine = machineInterval*machineId; 
		for (int i=startingMachine; i<startingMachine+machineInterval; i++){
			MultipleProfileSender mps = new MultipleProfileSender(comm,policies,i*interval,interval);
			mps.start();
		}
	}

	private static void uploadFunctions(Communicator comm, String function) {
		for (int i = 0; i < 1000; i++) {
			String username = String.format("user_%d@gmail.com", i);
			comm.sendData(new PolicyInfo(username, (username + function).split(",")));
		}
	}

	private static void generateRandomUser(Communicator comm) throws ParseException {
		Random rand = new Random(100);
		
		// create the users
		List<UserData> profiles = Generator.generateProfiles(IDs.DataID.VitalSignData.ordinal(), 1000);
		for (UserData pi : profiles){
			comm.sendData(pi);
		}
		/*
		// create friendship
		for (FriendInfo fi : Generator.generateFriends(1000))
			comm.sendData(fi);
		//for (int i = 0; i < 50; i++) {
		//	comm.sendData(new FriendInfo("user_0@gmail.com", String.format("user_%d@gmail.com", rand.nextInt(999) + 1), FriendInfo.ADD));
		//}
		*/
		// create vitalsign data	
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
		sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
		Date startingDate = sdf.parse(DEFAULT_DATE);
		List<VitalSignInfo> locs = Generator.generateVitalSignData(10,
				startingDate, 10, 10);
		for (VitalSignInfo loc : locs)
			comm.sendData(loc);
					
		System.out.println("Done uploading vital sign data");
	}
}

class MultipleProfileSender extends Thread{
	Communicator com;
	List<UserData> profiles;
	int startingIndex;
	int n;
	
	public MultipleProfileSender(Communicator com, List<UserData> profiles, int si, int n){
		this.com = com;
		this.profiles = profiles;
		this.startingIndex = si;
		this.n = n;
	}
	
	public void run(){
		UserData pi;
		for (int i=0; i<n; i++){
			pi = this.profiles.get(i+this.startingIndex);
			this.com.sendData(pi);
			System.out.println("sent "+(i+this.startingIndex));
		}
	}
}
